package de.hsb.ai.agent.misterstar;

import game.Move;

import java.util.List;
import java.util.Vector;

import org.jgrapht.Graphs;

import de.hsb.ai.agent.AbstractFugitive;
import de.hsb.ai.domain.GameOver;
import de.hsb.ai.domain.GameStarted;
import de.hsb.ai.domain.MoveEvent;
import de.hsb.ai.util.map.ScotlandJadeMapHelper.ScotlandJadeVertex;
import de.hsb.ai.util.map.ScotlandJadeMapHelper.ScotlandJadeVertex.Type;

@SuppressWarnings("serial")
public class MisterStar extends AbstractFugitive {
	MisterStarHelper mshelper;
	
	public MisterStar() {
		super("MisterStar");
		mshelper = new MisterStarHelper();
	}
	
	@Override
	protected void setup() {
		super.setup();
		
	}
	
	@Override
	protected void handleGameStarted(GameStarted g) {
		super.handleGameStarted(g);
	}
	
	@Override
	protected void handleGameOver(GameOver g) {
		super.handleGameOver(g);
	}
	
	@Override
	protected void handleMove(MoveEvent m) {
		super.handleMove(m);
		
	}
	
	@Override
	protected Move getMove(List<Move> options) {	
		//1. Get the Position of Detectives...
		//2. Get Mister X Options...
		//3. Count node from option to PlayerPosition...	
		//4. Calculate best Option...
		

		Move m = getBestOption(options);
		if(m != null)
		{
			return m;
		}
		else
		{
			System.out.println("...............Bad Calculation...............");
			return options.get(0);
		}
	}
	
	/*--------------Helper Methods-------------------*/
	
	/**
	 * Get the Positions of all Detectives
	 */
	private Vector<ScotlandJadeVertex> getDetectivePositions()
	{
		Vector<ScotlandJadeVertex> detectivePos = new Vector<ScotlandJadeVertex>();
		for(int i=0; i<positions.size();i++)
		{
			if(positions.get(i).getType() == Type.DETECTIVE)
			{
				detectivePos.add(positions.get(i));
			}
		}
		
		return detectivePos;
	}
	
//TODO: Implement A*
//	private int countNode(int from, int to)
//	{		
//		Node node = calculateChild(null, map.get(from),to);
//		while(node.getBestChild().getIndex() != to)
//		{ 
//			 
//			node = calculateChild(node, node.getBestChild().getNode(), to);
//			//Node bestNode = searchNextBest(node);
//			//if(bestNode != null)
//			//{
//			//	node = bestNode;
//			//}
//			
//		} 
//		
//		int countNodes = node.countParents();
//		node = null;
//		return countNodes;
//	}
//
//	private Node searchNextBest(Node node)
//	{
//		Node currNode = node; 
//		Node bestNode = null;
//		double min = -1;
//		while(node != null) 
//		{ 
//			if(node.getNextBestChild().getHeuristic() < currNode.getBestChild().getHeuristic() && node.getNextBestChild().getHeuristic() < min
//					|| node.getNextBestChild().getHeuristic() < currNode.getBestChild().getHeuristic() && min < 0 )
//			{
//				min = node.getNextBestChild().getHeuristic();
//				bestNode = node;
//			}
//			node = node.getParent(); 
//		}
//		if(bestNode != null)
//		{
//			bestNode.switchChild();
//			bestNode.setNextBestChild(currNode.getBestChild());
//		}
//		return bestNode;
//	}
//	
//	private Node calculateChild(Node parent, ScotlandJadeVertex vertex, int to)
//	{
//		Node node = new Node(parent, vertex, mshelper.getDistance(vertex.getIndex(), to));
//		if(parent!=null)
//			node.setG(parent.getBestChild().getG());
//		
//		List<ScotlandJadeVertex> childlist = Graphs.neighborListOf(map,node.getNode());
//		double 	mindistance = -1;
//		int 	minindex = -1;
//		for(int i=0; i<childlist.size(); i++)
//		{ 
//			//double distance = node.getG() + mshelper.getDistance(node.getIndex(), childlist.get(i).getIndex()) + mshelper.getDistance(childlist.get(i).getIndex(), map.get(to).getIndex());
//			double distance = mshelper.getDistance(childlist.get(i).getIndex(), map.get(to).getIndex());
//			
//			if(distance < mindistance || mindistance < 0)
//			{
//				mindistance = distance;
//				minindex = childlist.get(i).getIndex();
//			} 
//		}
//	 
//		node.setBestChild(new Node(node, map.get(minindex), mshelper.getDistance(minindex, map.get(to).getIndex())));
//		node.getBestChild().setG(node.getG() + mshelper.getDistance(node.getIndex(), minindex));
//		
//		double 	nextmindistance = -1;
//		int 	nextminindex = -1;
//		if(childlist.size() > 0)
//		{
//			for(int i=0; i<childlist.size(); i++)
//			{
//				//double distance = node.getG() + mshelper.getDistance(node.getIndex(), childlist.get(i).getIndex()) + mshelper.getDistance(childlist.get(i).getIndex(), map.get(to).getIndex());
//				double distance = mshelper.getDistance(childlist.get(i).getIndex(), map.get(to).getIndex());
//				
//				if(distance != mindistance && distance < nextmindistance || distance != mindistance && nextmindistance < 0)
//				{
//					nextmindistance = distance;
//					nextminindex = childlist.get(i).getIndex();
//				}
//			}
//		}
// 
//		node.setNextBestChild(new Node(node, map.get(nextminindex), mshelper.getDistance(nextminindex, map.get(to).getIndex())));
//		node.getNextBestChild().setG(node.getG() + mshelper.getDistance(node.getIndex(), nextminindex));
//		
//		return node;
//	}
	
	//Not optimal because of greedy
	public int countNode(int from, int to)
	{
		int currNode = from;
		int countNode = 0;
		
		while(currNode != to)
		{
			List<ScotlandJadeVertex> childlist = Graphs.neighborListOf(map,map.get(currNode));
			
			double min = -1;
			double distance = 0;
			for(int i=0; i<childlist.size(); i++)
			{
				distance =  mshelper.getDistance(childlist.get(i).getIndex(), to);
				if(distance < min || min < 0)
				{
					min = distance;
					currNode = childlist.get(i).getIndex();
				}
				if(childlist.get(i).getIndex() == to)
				{
					currNode = to;
					break;
				}
			}
			
			countNode++;
			if(countNode>30)
			{
				break;
			}
		}
		return countNode;
	}
	

	/**
	 * Calculate best Option for MrX<br>
	 * 1. Get the position of the detectives<br>
	 * 2. Count the nodes from the the option to the detectives (SMA* algorithm)<br>
	 * 3. Save the sum of the Options in a list<br>
	 * 4. Calculate the distance sum  of the options and put it in a array {@link #getDistanceSum getDistanceSum}
	 * 5. Get the Difference between the max(options) and every option<br>
	 * 6. Add the distance to the distance sum<br>
	 * 7. Return the Value with the index of the min(distanceSum)<br>
	 * 8. Return the option of the that index<br>
	 * @param options The options of the Player
	 * @return The best Option Move
	 */
	public Move getBestOption(List<Move> options)
	{
		// Get the positions of the detectives
		Vector<ScotlandJadeVertex> detectivePos = getDetectivePositions();
		
		// Initialize a 2-dimensional array for the nodes
		Vector<Vector<Integer>> nodes = new Vector<Vector<Integer>>();
		
		// Count the nodes for every option to the detective -> put in 2D array
		for(int i=0; i<options.size(); i++)
		{
			//Add a new vector list...
			nodes.add(new Vector<Integer>());
			for(int j=0; j<detectivePos.size();j++)
			{
				//Add nodecount to vector list...
				nodes.get(i).add(countNode(detectivePos.get(j).getIndex(),options.get(i).getNode()));
			}
		}
		
		//Sum the nodes put in a array
		Vector<Integer> sum = new Vector<Integer>();
		int summax = -1;
		for(int i=0; i<nodes.size(); i++)
		{
			int summe = 0;
			for(int j=0; j<nodes.get(i).size(); j++)
			{
				summe += nodes.get(i).get(j);
			}
			//Get the max sum for calculation
			if(summe > summax)
			{
				summax = summe;
			}
			
			//Put the sum in the array
			sum.add(summe);
		}
		
		//Get the distance sum of the nodes
		Vector<Integer> distance = new Vector<Integer>();
		for(int i=0; i<nodes.size(); i++)
		{
			distance.add(getDistanceSum(nodes.get(i)));
		}
		
		//Get distance between the nodes
		int minDistanceSum = -1;
		int index = -1;
		for(int i=0; i<nodes.size(); i++)
		{
			//Round the distance to the difference between the max sum and every sum
			int difference = Math.abs(sum.get(i) - summax);
			distance.set(i, distance.get(i)+difference);
			
			//Get the min distance 
			if(distance.get(i) < minDistanceSum || minDistanceSum <0)
			{
				minDistanceSum = distance.get(i);
				//Store the distance of min distance
				index = i;
			}
		}
		
		//return the option of the min index
		if(index >= 0)
		{
			return options.get(index);
		}
		else
			return null;
	}
	/**
	 * Calculate the distance in a List...
	 * list = {2,3,8} distanceSum = |2-3| + |2-8| + |3-8|
	 * @param list
	 * @return distanceSum
	 */
	private int getDistanceSum(Vector<Integer> list)
	{
		int sum=0;
		for(int i=0; i<list.size(); i++)
		{
			for(int j=i+1; j<list.size();j++)
			{
				sum += Math.abs(list.get(i) - list.get(j));
			}
		}
		
		return sum;
	}
	
}
